Перейти к основному содержимому

Фреймворки и инструменты Rust

Разработчику Архитектору

Фреймворки и инструменты Rust

О фреймворках и библиотеках

Фреймворки в Rust — это программные основы, предоставляющие разработчикам готовую архитектуру, набор инструментов и соглашения для построения приложений. Они формируют контекст, в котором пишется код, определяют структуру проекта, управляют зависимостями, обеспечивают повторное использование компонентов и задают правила взаимодействия между частями программы. В экосистеме Rust фреймворки не доминируют в том же смысле, как в некоторых других языках, например в Python или JavaScript. Это связано с философией самого языка: Rust стремится к минимализму, предсказуемости и явному контролю со стороны программиста. Тем не менее, фреймворки в Rust существуют, развиваются и решают важные задачи — от веб-разработки до системного программирования, от асинхронных сетевых приложений до встраиваемых устройств.

В Rust чаще используются библиотеки, а не монолитные фреймворки. Это принципиальное различие. Библиотека — это набор функций, типов и модулей, которые можно подключить к проекту и вызывать по мере необходимости. Фреймворк — это среда, которая управляет ходом выполнения программы и требует от разработчика соблюдения определённых правил и шаблонов. В Rust преобладает подход «библиотечной композиции»: разработчик самостоятельно собирает приложение из независимых, хорошо спроектированных компонентов, каждый из которых решает свою узкую задачу. Такой подход даёт гибкость, контроль над производительностью и возможность точно настраивать поведение системы. Однако в некоторых областях, особенно там, где требуется высокая степень стандартизации или сложная координация между компонентами, фреймворки становятся полезными и даже необходимыми.

Одним из ключевых свойств фреймворков в Rust является их соответствие системе владения (ownership) и заимствования (borrowing). Эта система — центральный элемент безопасности памяти в Rust — влияет на то, как проектируются интерфейсы, как передаются данные между компонентами и как обрабатываются ошибки. Фреймворки в Rust вынуждены работать внутри этих ограничений, что делает их более строгими, но и более надёжными. Они не могут скрывать неопределённое поведение, скрытые аллокации или потенциальные гонки данных. Вместо этого они предлагают API, которые явно выражают жизненные циклы ресурсов, гарантируют безопасность потоков и позволяют компилятору проверять корректность программы ещё до запуска.

Рассмотрим основные категории фреймворков в Rust и их представителей.


Веб-фреймворки

Веб-фреймворки — наиболее развитая область фреймворков в Rust. Здесь можно выделить несколько уровней абстракции. На нижнем уровне находятся такие решения, как hyper — это низкоуровневый HTTP-сервер и клиент, реализующий протокол с минимальными накладными расходами. Он не навязывает структуру приложения, но предоставляет мощные примитивы для работы с соединениями, заголовками, телами запросов и ответов. Над hyper строятся более высокоуровневые фреймворки, такие как Axum, Warp, Rocket и Actix Web.

Axum

Axum — современный фреймворк, разработанный командой Tokio. Он использует типизированные маршруты, middleware на основе трейтов и глубоко интегрирован с экосистемой асинхронного программирования в Rust. Axum делает акцент на композируемости: каждый обработчик запроса — это функция, которая принимает типизированные входные данные и возвращает типизированный результат. Это позволяет компилятору проверять корректность маршрутов и обработчиков на этапе сборки. Middleware в Axum реализуется через расширение типов с помощью трейтов, что обеспечивает высокую производительность без динамической диспетчеризации.

Процесс установки фреймворка Axum начинается с добавления необходимых зависимостей в файл конфигурации проекта Cargo.toml. В секцию [dependencies] необходимо включить пакет axum и его основные зависимости, такие как tokio для асинхронного выполнения. Файл конфигурации получает строку axum = "0.7" (или актуальную версию) и tokio = { version = "1", features = ["full"] }. После сохранения файла конфигурации команда cargo build или cargo check автоматически скачивает все необходимые библиотеки из реестра crates.io и компилирует их под текущую архитектуру системы.

Для работы с HTTP-запросами и ответами часто подключают вспомогательные пакеты, например serde для сериализации данных и tower-http для расширения функционала middleware. Эти зависимости также указываются в том же файле Cargo.toml перед запуском процесса сборки. Компилятор Rust гарантирует совместимость версий всех подключенных библиотек и сообщает об ошибках, если версии не соответствуют друг другу.

Создание сервера на базе Axum начинается с импорта необходимых модулей в корневом файле программы main.rs. Код импортирует функции axum::Server и axum::Router, которые служат точками входа для запуска веб-сервиса. Импортируются также трейты IntoResponse и типы запросов, такие как Json, Query или Path, которые позволяют обработчикам принимать структурированные данные от клиента.

Основой приложения становится создание экземпляра маршрутизатора Router. Этот объект собирает набор правил обработки запросов, где каждый маршрут сопоставляется с конкретной функцией-обработчиком. Обработчик принимает типизированные аргументы, соответствующие параметрам URL или телу запроса, и возвращает результат, который автоматически преобразуется в HTTP-ответ. Компилятор проверяет соответствие типов аргументов и возвращаемых значений на этапе компиляции, исключая ошибки времени выполнения, связанные с неверной передачей данных.

Подключение middleware происходит путем вызова метода .layer() на объекте маршрутизатора. Эта операция оборачивает цепочку обработки запроса дополнительными слоями логики, такими как логирование, обработка ошибок или управление заголовками CORS. Каждый слой реализует трейт Layer и выполняет свою задачу до передачи управления следующему элементу цепочки. Порядок подключения слоев определяет последовательность их выполнения при обработке входящего запроса.

Запуск сервера осуществляется через статический метод Server::bind с указанием адреса и порта, к которому будет привязан сервис. Метод .serve() принимает роутер и запускает цикл обработки событий сети. Процесс выполняется внутри цикла асинхронности Tokio, что позволяет серверу обрабатывать тысячи одновременных соединений без блокировки потока. Приложение продолжает работать до момента получения сигнала о завершении или возникновения критической ошибки.


Warp

Warp — фреймворк, построенный на концепции «фильтров». Каждый фильтр — это компонент, который может извлекать данные из запроса, проверять условия, преобразовывать данные или изменять поведение ответа. Фильтры комбинируются с помощью операторов, образуя сложные конвейеры обработки. Этот подход позволяет декларативно описывать логику маршрутизации и обработки, сохраняя при этом типобезопасность. Warp также полностью асинхронный и работает поверх hyper.

Процесс установки фреймворка Warp начинается с редактирования файла конфигурации проекта Cargo.toml. В секцию [dependencies] необходимо добавить строку, указывающую на пакет warp и его версию. Стандартная практика подразумевает использование актуальной стабильной версии, например warp = "0.3". Фреймворк имеет зависимость от библиотеки hyper, которая подтягивается автоматически как транзитивная зависимость, но часто требуется явное подключение пакета tokio для управления асинхронным выполнением. В файл добавляется запись tokio = { version = "1", features = ["full"] }. После сохранения изменений команда cargo build скачивает необходимые компоненты из реестра crates.io и компилирует их в бинарный файл проекта.

Для работы с данными внутри фильтров рекомендуется подключить пакет serde для сериализации JSON-структур. Это позволяет легко преобразовывать входящие данные запроса в типизированные структуры Rust и обратно в ответы серверу. Пакет также указывается в секции зависимостей: serde = { version = "1", features = ["derive"] }. Компилятор проверяет совместимость всех версий и гарантирует отсутствие конфликтов между компонентами до начала сборки приложения.

Создание сервера на базе Warp начинается с импорта ключевых модулей в корневом файле main.rs. Код импортирует функции warp::serve и типы фильтров, такие как path, query, header или body. Эти элементы служат строительными блоками для конвейера обработки запросов. Импортируются также трейты из serde для определения структур данных, которые будут использоваться в качестве параметров маршрутов или тела запроса.

Основой приложения становится построение цепочки фильтров с помощью операторов композиции. Фильтр path извлекает часть URL-адреса, фильтр query парит параметры строки запроса, а фильтр header проверяет наличие заголовков. Операторы and и or объединяют эти компоненты в единую логику проверки условий. Результатом такой комбинации становится новый фильтр, который содержит полную информацию о валидности запроса и доступных данных. Компилятор Rust обеспечивает типобезопасность всей цепочки, гарантируя, что каждый этап получает данные ожидаемого формата.

После формирования логического фильтра к нему применяется обработчик — обычная функция, которая принимает извлеченные данные и возвращает результат. Обработчик может использовать функцию reply() для создания ответа, содержащего текст, статус-код или сериализованный JSON. Этот процесс происходит без явного создания объектов HTTP-запроса, так как данные передаются напрямую через стек вызовов фильтров. Такой подход устраняет необходимость в ручной проверке типов и снижает количество кода, связанного с обработкой ошибок ввода.

Запуск сервера осуществляется через вызов метода serve у объекта warp::serve. Метод принимает построенный ранее конвейер фильтров и привязывает его к указанному адресу и порту. Процесс выполняется внутри цикла асинхронности Tokio, что позволяет серверу эффективно обрабатывать множество одновременных соединений. Приложение продолжает работу до момента получения сигнала остановки или возникновения критической ошибки в системе.


Rocket

Rocket — фреймворк, ориентированный на удобство и эргономику. Он использует макросы для автоматической генерации кода маршрутизации, сериализации и десериализации. Rocket поддерживает параметры пути, заголовки, куки, формы и JSON «из коробки». Он стремится сделать разработку максимально похожей на написание обычных функций, скрывая сложность HTTP-протокола за простым и выразительным API. Rocket долгое время был синхронным, но в новых версиях добавлена поддержка асинхронности.

Процесс установки фреймворка Rocket начинается с открытия файла конфигурации проекта Cargo.toml. В секцию [dependencies] необходимо добавить строку, указывающую на пакет rocket и его актуальную версию, например rocket = "0.5". Фреймворк имеет строгие требования к версии компилятора Rust, поэтому рекомендуется использовать стабильную ветку (например, 1.70 или новее). Также в зависимости часто добавляют пакеты для работы с JSON-данными, такие как serde и serde_json, которые позволяют легко сериализовать и десериализровать данные без написания дополнительного кода. После добавления записей в файл конфигурации команда cargo build автоматически скачивает все необходимые библиотеки из реестра crates.io и компилирует их.

Для корректной работы асинхронных функций в новых версиях Rocket требуется подключить библиотеку tokio с соответствующими фичами. В файл Cargo.toml добавляют запись tokio = { version = "1", features = ["full"] }. Компилятор проверяет совместимость всех версий компонентов и гарантирует отсутствие конфликтов перед началом сборки приложения. Если используются дополнительные типы данных, такие как временные метки или специфические структуры, их также подключают через зависимости chrono или другие утилиты.

Создание сервера на базе Rocket начинается с импорта макроса launch и основных типов в корневом файле main.rs. Макрос #[launch] служит точкой входа и автоматически генерирует код для запуска цикла обработки событий сети. Код импортирует трейты Json, Query, Path и Form, которые позволяют обработчикам принимать типизированные данные от клиента. Эти типы работают как маркеры для автоматической валидации входных параметров запроса.

Основой приложения становится определение маршрутов с использованием макроса get, post, put или delete. Каждый маршрут объявляется функцией, параметры которой аннотированы специальными атрибутами, такими как #[path], #[query], #[header] или #[body]. Макрос анализирует эти аннотации и генерирует необходимый код для парсинга входящих данных, проверки их типов и преобразования в целевые структуры Rust. Это позволяет разработчику писать функции, которые выглядят как обычные методы, не беспокоясь о деталях HTTP-протокола.

Обработчик принимает данные напрямую в виде типизированных аргументов и возвращает результат, который автоматически конвертируется в HTTP-ответ. Например, функция может вернуть структуру Json<T>, и Rocket сам выполнит сериализацию в формат JSON и установит правильный заголовок контента. Такой подход устраняет необходимость в ручном создании объектов ответа и управлении статус-кодами. Ошибки валидации данных перехватываются фреймворком и преобразуются в стандартные ответы с кодом ошибки 400.

Запуск сервера происходит автоматически при выполнении макроса launch. Приложение привязывается к указанному адресу и порту, после чего начинает слушать входящие соединения. Процесс выполняется внутри цикла асинхронности Tokio, что обеспечивает высокую производительность при обработке множества запросов. Сервис продолжает работать до момента получения сигнала остановки или возникновения критической ошибки в системе.


Actix Web

Actix Web — один из самых популярных и производительных веб-фреймворков в Rust. Он основан на акторной модели, хотя в повседневном использовании эта модель часто остаётся в тени. Actix Web предлагает мощную систему маршрутизации, встроенную поддержку middleware, работу с вебсокетами, статическими файлами и фоновыми задачами. Он отличается высокой скоростью обработки запросов и низким потреблением памяти, что делает его подходящим для микросервисов и высоконагруженных систем.

Процесс установки фреймворка Actix Web начинается с редактирования файла конфигурации проекта Cargo.toml. В секцию [dependencies] необходимо добавить строку, указывающую на пакет actix-web и его актуальную версию, например actix-web = "4". Для корректной работы сервера требуется подключить библиотеку actix-rt, которая предоставляет runtime для асинхронных операций. В файл также добавляют запись actix-rt = "2" или аналогичную версию. Компилятор Rust автоматически разрешает зависимости и скачивает необходимые компоненты из реестра crates.io при выполнении команды cargo build.

Для работы с данными часто подключают пакеты serde и serde_json, которые позволяют легко сериализовать и десериализровать JSON-структуры. Эти зависимости добавляются в тот же файл конфигурации: serde = { version = "1", features = ["derive"] }. Компилятор проверяет совместимость всех версий компонентов и гарантирует отсутствие конфликтов перед началом сборки приложения. Если планируется работа с вебсокетами или другими расширенными функциями, могут потребоваться дополнительные пакеты, такие как actix-web-actors или actix-files, которые также указываются в списке зависимостей.

Создание сервера на базе Actix Web начинается с импорта ключевых модулей в корневом файле main.rs. Код импортирует функции web::block и типы маршрутизации, такие как get, post, put или delete. Импортируются также трейты Responder и типы данных, например Json, Query или Path, которые позволяют обработчикам принимать типизированные данные от клиента. Эти элементы служат строительными блоками для построения логики обработки запросов.

Основой приложения становится создание экземпляра приложения через функцию App::new. Этот объект собирает набор правил обработки запросов, где каждый маршрут сопоставляется с конкретной функцией-обработчиком. Обработчик принимает типизированные аргументы, соответствующие параметрам URL или телу запроса, и возвращает результат, который автоматически преобразуется в HTTP-ответ. Компилятор Rust обеспечивает проверку типов на этапе компиляции, исключая ошибки времени выполнения, связанные с неверной передачей данных.

Подключение middleware происходит путем вызова метода .app_data() или использования трейта Transform для внедрения дополнительных слоев логики. Это позволяет добавлять функциональность логирования, аутентификации, управления заголовками CORS или обработки ошибок. Каждый слой выполняет свою задачу до передачи управления следующему элементу цепочки. Порядок подключения слоев определяет последовательность их выполнения при обработке входящего запроса.

Запуск сервера осуществляется через функцию actix_web::HttpServer, которая принимает замыкание для создания нового экземпляра приложения. Метод .bind() привязывает сервер к указанному адресу и порту, после чего вызывается метод .run(). Процесс выполняется внутри цикла асинхронности Tokio, что позволяет серверу эффективно обрабатывать тысячи одновременных соединений без блокировки потока. Приложение продолжает работать до момента получения сигнала о завершении или возникновения критической ошибки.

Все эти фреймворки объединяет стремление к нулевым накладным расходам, полной типобезопасности и интеграции с асинхронной моделью Rust. Они не пытаются скрыть сложность, а предоставляют инструменты, которые позволяют управлять ею явно и безопасно.


Фреймворки для системного программирования

Фреймворки для системного программирования в Rust встречаются реже, поскольку сам язык уже предоставляет низкоуровневый контроль. Однако существуют решения, которые упрощают разработку драйверов, демонов или встраиваемых приложений.

Например, tokio и async-std — это не фреймворки в классическом смысле, но они создают среду выполнения для асинхронных приложений, управляя событиями, задачами и ресурсами. Они задают правила того, как писать асинхронный код, и предоставляют стандартные примитивы: каналы, таймеры, мьютексы, пулы потоков. В этом смысле они выполняют роль фреймворка для асинхронного программирования.

Для встраиваемых систем Rust предлагает такие фреймворки, как RTIC (Real-Time Interrupt-driven Concurrency) — это система для написания реального времени приложений на микроконтроллерах. RTIC управляет приоритетами прерываний, обеспечивает безопасный доступ к разделяемым ресурсам и позволяет писать конкурентный код без блокировок. Он использует макросы и анализ времени компиляции, чтобы гарантировать отсутствие гонок данных и взаимоблокировок.


Фреймворки для тестирования и отладки

Фреймворки для тестирования и отладки также играют важную роль. Хотя cargo предоставляет встроенную поддержку unit- и integration-тестов, существуют фреймворки, расширяющие эти возможности.

Например, criterion — это фреймворк для бенчмаркинга, который автоматически собирает статистику, строит графики и сравнивает производительность разных версий кода.

proptest — фреймворк для property-based Тестирование, который генерирует случайные входные данные и проверяет, что программа удовлетворяет заданным инвариантам. Эти инструменты формируют методологию проверки корректности и эффективности программ, что особенно важно в системах, где ошибки могут иметь серьёзные последствия.


Фреймворки для CLI-приложений

Фреймворки для CLI-приложений — ещё одна категория.

clap — это библиотека, но она настолько мощная и структурированная, что часто воспринимается как фреймворк. Она позволяет описывать интерфейс командной строки с помощью декларативного DSL, автоматически генерирует справку, проверяет аргументы и поддерживает подкоманды, флаги и опции. С её помощью можно быстро создать профессиональное CLI-приложение с минимальными усилиями.

Особенность фреймворков в Rust заключается в их модульности. Даже если используется фреймворк, разработчик может заменить любой его компонент на альтернативный, если это необходимо.

Например, в Axum можно использовать любой сериализатор, совместимый с serde, или подключить собственный middleware вместо стандартного. Эта гибкость — следствие философии Rust, которая ценит явность, композицию и контроль.

Ещё один важный аспект — экосистема. Фреймворки в Rust тесно интегрированы с менеджером пакетов cargo и реестром crates.io. Это позволяет легко управлять зависимостями, обновлять версии и делиться компонентами. Многие фреймворки предоставляют шаблоны (templates) или генераторы проектов (cargo-generate), которые помогают начать разработку с правильной структуры.


Фреймворки для десктопных приложений

В Rust экосистема десктопных фреймворков развивается активно, но с акцентом на кроссплатформенность, производительность и минимальную зависимость от нативных API. Основной вызов здесь — создание графического интерфейса, который работает одинаково на Windows, macOS и Linux, не теряя при этом отзывчивости и эстетики. В отличие от Java или Electron, где абстракции часто ведут к увеличению потребления памяти, Rust-решения стремятся к прямому взаимодействию с системными библиотеками через FFI (Foreign Function Interface), сохраняя контроль над ресурсами.

Tauri — один из самых заметных проектов в этой области. Он сочетает в себе легковесный бэкенд на Rust с фронтендом на HTML/CSS/JavaScript, запускаемым внутри системного WebView (например, WebKit на macOS, WebView2 на Windows). Tauri не включает собственный движок рендеринга, как Electron, а полагается на уже установленные компоненты операционной системы. Это делает приложения значительно компактнее: типичное Tauri-приложение может занимать менее 5 МБ, тогда как аналог на Electron — десятки или сотни мегабайт. Taurи предоставляет безопасный API для вызова Rust-функций из JavaScript, управляет правами доступа, поддерживает сборку под все основные платформы и интегрируется с системными уведомлениями, меню, файловыми диалогами. Архитектура Tauri построена на принципе «минимального доверия»: JavaScript-код не имеет прямого доступа к файловой системе или сетевым ресурсам без явного разрешения со стороны Rust-ядра.

Iced — это кроссплатформенный GUI-фреймворк, вдохновлённый Elm Architecture. Он использует реактивную модель: интерфейс описывается как функция состояния, а любое взаимодействие пользователя порождает сообщение, которое обрабатывается центральным обработчиком и обновляет состояние. Iced полностью написан на Rust и не зависит от WebView или JavaScript. Он рендерит интерфейс через wgpu — кроссплатформенную графическую библиотеку, которая абстрагирует Vulkan, Metal, DirectX и OpenGL. Это позволяет Iced работать даже на встраиваемых устройствах с GPU. Iced подходит для приложений, где важна предсказуемость поведения и полный контроль над логикой, но требует от разработчика понимания функционального подхода к UI.

Slint — ещё один декларативный фреймворк, но с собственным DSL (Domain-Specific Language) для описания интерфейсов. Slint-файлы напоминают QML из Qt: они содержат иерархию компонентов, привязки свойств, анимации и обработчики событий. Компилятор Slint генерирует Rust-код, который можно встроить в проект. Slint оптимизирован для встраиваемых систем и устройств с ограниченными ресурсами, но также отлично работает на десктопе. Он поддерживает темы, адаптивные макеты и аппаратное ускорение рендеринга. Slint особенно популярен в промышленной автоматизации и HMI (Human-Machine Interface).

Dioxus — фреймворк, который переносит концепции React в Rust. Он использует JSX-подобный синтаксис (через макросы) и виртуальное дерево компонентов, но исполняется полностью на Rust. Dioxus может выводить интерфейс не только в десктопные окна (через wry или tao), но и в веб (WebAssembly), мобильные приложения (через dioxus-mobile) и даже в терминал (dioxus-tui). Это делает его универсальным инструментом для кроссплатформенной разработки с единым кодом. Dioxus управляет жизненным циклом компонентов, поддерживает хуки (hooks) и асинхронные эффекты, что знакомо разработчикам из экосистемы JavaScript.

Druid и его преемник Floem представляют более традиционный подход к нативному UI. Druid был одним из первых попыток создать чисто Rust-овый GUI-фреймворк с акцентом на данные и производительность. Floem, созданный теми же авторами, уходит от сложной архитектуры Druid в сторону простоты и скорости. Floem использует императивный стиль, но с элементами реактивности: изменения данных автоматически приводят к перерисовке только затронутых частей интерфейса. Floem ориентирован на создание высокопроизводительных приложений, таких как редакторы кода или аудио-процессоры, где важна минимальная задержка и максимальная отзывчивость.

Все эти фреймворки объединяет стремление к явному управлению состоянием, типобезопасности и отсутствию скрытых побочных эффектов. Они не прячут сложность за магическими методами, а предоставляют разработчику инструменты, которые позволяют строить интерфейсы с полным пониманием того, как они работают под капотом.


Фреймворки для серверных и фоновых служб

Серверная разработка — одна из сильнейших областей Rust. Здесь доминируют асинхронные фреймворки, построенные поверх tokio или async-std. Эти фреймворки обеспечивают высокую пропускную способность, низкую задержку и эффективное использование ресурсов, что делает Rust конкурентоспособным с Go и C++ в микросервисной архитектуре.

Actix Web остаётся лидером по производительности. Он использует акторную модель, где каждый HTTP-обработчик может быть представлен как актор, обрабатывающий сообщения. Actix Web поддерживает HTTP/1, HTTP/2, WebSockets, SSE (Server-Sent Events), middleware, CORS, сессии, кэширование и многое другое. Он легко интегрируется с базами данных через sqlx или diesel, с очередями через redis или rabbitmq, и с фоновыми задачами через tokio::task.

Axum — современный фреймворк от команды Tokio. Он построен на hyper и tower, и использует типизированные маршруты через макрос Router. Axum делает акцент на композируемости: каждый обработчик — это функция, которая принимает типизированные входные данные (например, Json<User>, Path<String>, State<AppState>) и возвращает типизированный результат. Это позволяет компилятору проверять корректность маршрутов на этапе сборки. Middleware в Axum реализуется через трейты FromRequest и IntoResponse, что обеспечивает нулевые накладные расходы.

Rocket — фреймворк, ориентированный на удобство. Он использует макросы для автоматической регистрации маршрутов, сериализации и валидации. Rocket поддерживает параметры пути, формы, JSON, куки, заголовки, CORS, TLS и даже встроенный dev-сервер с горячей перезагрузкой. Несмотря на удобство, Rocket не жертвует безопасностью: все входные данные проверяются на соответствие типам, а ошибки обрабатываются явно.

Warp — фреймворк, основанный на концепции «фильтров». Фильтр — это компонент, который может извлекать данные из запроса, проверять условия или преобразовывать ответ. Фильтры комбинируются с помощью операторов (and, or, map, with), образуя сложные конвейеры. Warp полностью асинхронный, работает поверх hyper, и идеально подходит для создания REST API и микросервисов с минимальным количеством кода.

Для фоновых задач Rust предлагает не фреймворки, а библиотечные примитивы: tokio::task::spawn для запуска асинхронных задач, async-channel для межзадачного взаимодействия, tokio-cron или cron-parser для планирования по расписанию, sqlx + pg_cron для интеграции с PostgreSQL. Такой подход позволяет собрать фоновую службу любой сложности, не привязываясь к жёсткой архитектуре.


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).